<!DOCTYPE html>
<html lang="en-US">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>ZRender 源码解析 | 小熊的技术文档</title>
    <meta name="generator" content="VuePress 1.8.2">
    <link rel="icon" href="/fav.ico">
    <link rel="stylesheet" href="/css/katex.min.css">
    <link rel="stylesheet" href="/css/github-markdown.min.css">
    <meta name="description" content="衣带渐宽终不悔，为伊消得人憔悴">
    
    <link rel="preload" href="/assets/css/0.styles.e6449353.css" as="style"><link rel="preload" href="/assets/js/app.9e067b79.js" as="script"><link rel="preload" href="/assets/js/2.983eb755.js" as="script"><link rel="preload" href="/assets/js/70.11ee4e27.js" as="script"><link rel="prefetch" href="/assets/js/10.79548333.js"><link rel="prefetch" href="/assets/js/11.31600f80.js"><link rel="prefetch" href="/assets/js/12.62bd3528.js"><link rel="prefetch" href="/assets/js/13.de8b1ace.js"><link rel="prefetch" href="/assets/js/14.f5db1e75.js"><link rel="prefetch" href="/assets/js/15.bf827d4d.js"><link rel="prefetch" href="/assets/js/16.cc9a1a73.js"><link rel="prefetch" href="/assets/js/17.2cfaefeb.js"><link rel="prefetch" href="/assets/js/18.3af7782b.js"><link rel="prefetch" href="/assets/js/19.639f7e7b.js"><link rel="prefetch" href="/assets/js/20.dc862f59.js"><link rel="prefetch" href="/assets/js/21.916e1685.js"><link rel="prefetch" href="/assets/js/22.d7a955f4.js"><link rel="prefetch" href="/assets/js/23.078391ef.js"><link rel="prefetch" href="/assets/js/24.bb460a5a.js"><link rel="prefetch" href="/assets/js/25.d69f2326.js"><link rel="prefetch" href="/assets/js/26.6da7ea48.js"><link rel="prefetch" href="/assets/js/27.82e23d91.js"><link rel="prefetch" href="/assets/js/28.9073bbec.js"><link rel="prefetch" href="/assets/js/29.639259a4.js"><link rel="prefetch" href="/assets/js/3.e594e5b2.js"><link rel="prefetch" href="/assets/js/30.b49c622d.js"><link rel="prefetch" href="/assets/js/31.92f6c8b3.js"><link rel="prefetch" href="/assets/js/32.42419088.js"><link rel="prefetch" href="/assets/js/33.c82e2ab8.js"><link rel="prefetch" href="/assets/js/34.381de37e.js"><link rel="prefetch" href="/assets/js/35.5e86d478.js"><link rel="prefetch" href="/assets/js/36.31f218ce.js"><link rel="prefetch" href="/assets/js/37.0d287b3f.js"><link rel="prefetch" href="/assets/js/38.1324cf44.js"><link rel="prefetch" href="/assets/js/39.de6bdb51.js"><link rel="prefetch" href="/assets/js/4.440c4dd9.js"><link rel="prefetch" href="/assets/js/40.a22c9c27.js"><link rel="prefetch" href="/assets/js/41.4637d617.js"><link rel="prefetch" href="/assets/js/42.db815e2b.js"><link rel="prefetch" href="/assets/js/43.f0955a92.js"><link rel="prefetch" href="/assets/js/44.7d5faddf.js"><link rel="prefetch" href="/assets/js/45.a88ffc33.js"><link rel="prefetch" href="/assets/js/46.883caa71.js"><link rel="prefetch" href="/assets/js/47.6f2cfd60.js"><link rel="prefetch" href="/assets/js/48.ea25654e.js"><link rel="prefetch" href="/assets/js/49.f38c23a0.js"><link rel="prefetch" href="/assets/js/5.e8844f36.js"><link rel="prefetch" href="/assets/js/50.e03dd8e9.js"><link rel="prefetch" href="/assets/js/51.f6160d52.js"><link rel="prefetch" href="/assets/js/52.4daa8322.js"><link rel="prefetch" href="/assets/js/53.b30992e9.js"><link rel="prefetch" href="/assets/js/54.209f17e1.js"><link rel="prefetch" href="/assets/js/55.4f1dd95b.js"><link rel="prefetch" href="/assets/js/56.147ea3e8.js"><link rel="prefetch" href="/assets/js/57.5823e0e2.js"><link rel="prefetch" href="/assets/js/58.772320f9.js"><link rel="prefetch" href="/assets/js/59.5ab55a80.js"><link rel="prefetch" href="/assets/js/6.54e1cc95.js"><link rel="prefetch" href="/assets/js/60.b47b75bb.js"><link rel="prefetch" href="/assets/js/61.0cd5a012.js"><link rel="prefetch" href="/assets/js/62.35eb5453.js"><link rel="prefetch" href="/assets/js/63.d31f2118.js"><link rel="prefetch" href="/assets/js/64.03d35d7c.js"><link rel="prefetch" href="/assets/js/65.8bdc633f.js"><link rel="prefetch" href="/assets/js/66.dbbe8867.js"><link rel="prefetch" href="/assets/js/67.228613b5.js"><link rel="prefetch" href="/assets/js/68.d10a2687.js"><link rel="prefetch" href="/assets/js/69.7903847f.js"><link rel="prefetch" href="/assets/js/7.f5ab00eb.js"><link rel="prefetch" href="/assets/js/8.95b39934.js"><link rel="prefetch" href="/assets/js/9.26cfd48c.js">
    <link rel="stylesheet" href="/assets/css/0.styles.e6449353.css">
  </head>
  <body>
    <div id="app" data-server-rendered="true"><div class="theme-container"><header class="navbar"><div class="sidebar-button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" role="img" viewBox="0 0 448 512" class="icon"><path fill="currentColor" d="M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z"></path></svg></div> <a href="/" class="home-link router-link-active"><!----> <span class="site-name">小熊的技术文档</span></a> <div class="links"><div class="search-box"><input aria-label="Search" autocomplete="off" spellcheck="false" value=""> <!----></div> <nav class="nav-links can-hide"><div class="nav-item"><a href="/front-end/" class="nav-link">
  🎨前端
</a></div><div class="nav-item"><a href="/back-end/" class="nav-link">
  💻后端
</a></div><div class="nav-item"><a href="/practice/" class="nav-link router-link-active">
  🚀实战
</a></div><div class="nav-item"><a href="/office/" class="nav-link">
  🏢办公
</a></div><div class="nav-item"><a href="/general/" class="nav-link">
  🍓通用
</a></div><div class="nav-item"><a href="/paper/" class="nav-link">
  🐸论文
</a></div><div class="nav-item"><a href="/general/fast.html" class="nav-link">
  ⚡快速笔记
</a></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="🦉近期重点" class="dropdown-title"><span class="title">🦉近期重点</span> <span class="arrow down"></span></button> <button type="button" aria-label="🦉近期重点" class="mobile-dropdown-title"><span class="title">🦉近期重点</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/back-end/python.html" class="nav-link">
  🐇python常用模块
</a></li><li class="dropdown-item"><!----> <a href="/practice/zrender.html" aria-current="page" class="nav-link router-link-exact-active router-link-active">
  🌹zrender源码解析
</a></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="⭐️资源" class="dropdown-title"><span class="title">⭐️资源</span> <span class="arrow down"></span></button> <button type="button" aria-label="⭐️资源" class="mobile-dropdown-title"><span class="title">⭐️资源</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="https://www.birdiesearch.com/" target="_blank" rel="noopener noreferrer" class="nav-link external">
  小鸟搜索
  <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li><li class="dropdown-item"><!----> <a href="https://salttiger.com/" target="_blank" rel="noopener noreferrer" class="nav-link external">
  每天一本编程书
  <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li><li class="dropdown-item"><!----> <a href="https://gitee.com/docmirror/dev-sidecar" target="_blank" rel="noopener noreferrer" class="nav-link external">
  开发者边车
  <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li></ul></div></div> <!----></nav></div></header> <div class="sidebar-mask"></div> <aside class="sidebar"><nav class="nav-links"><div class="nav-item"><a href="/front-end/" class="nav-link">
  🎨前端
</a></div><div class="nav-item"><a href="/back-end/" class="nav-link">
  💻后端
</a></div><div class="nav-item"><a href="/practice/" class="nav-link router-link-active">
  🚀实战
</a></div><div class="nav-item"><a href="/office/" class="nav-link">
  🏢办公
</a></div><div class="nav-item"><a href="/general/" class="nav-link">
  🍓通用
</a></div><div class="nav-item"><a href="/paper/" class="nav-link">
  🐸论文
</a></div><div class="nav-item"><a href="/general/fast.html" class="nav-link">
  ⚡快速笔记
</a></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="🦉近期重点" class="dropdown-title"><span class="title">🦉近期重点</span> <span class="arrow down"></span></button> <button type="button" aria-label="🦉近期重点" class="mobile-dropdown-title"><span class="title">🦉近期重点</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/back-end/python.html" class="nav-link">
  🐇python常用模块
</a></li><li class="dropdown-item"><!----> <a href="/practice/zrender.html" aria-current="page" class="nav-link router-link-exact-active router-link-active">
  🌹zrender源码解析
</a></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="⭐️资源" class="dropdown-title"><span class="title">⭐️资源</span> <span class="arrow down"></span></button> <button type="button" aria-label="⭐️资源" class="mobile-dropdown-title"><span class="title">⭐️资源</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="https://www.birdiesearch.com/" target="_blank" rel="noopener noreferrer" class="nav-link external">
  小鸟搜索
  <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li><li class="dropdown-item"><!----> <a href="https://salttiger.com/" target="_blank" rel="noopener noreferrer" class="nav-link external">
  每天一本编程书
  <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li><li class="dropdown-item"><!----> <a href="https://gitee.com/docmirror/dev-sidecar" target="_blank" rel="noopener noreferrer" class="nav-link external">
  开发者边车
  <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li></ul></div></div> <!----></nav>  <ul class="sidebar-links"><li><a href="/practice/deepL.html" class="sidebar-link">使用pytorch进行深度学习</a></li><li><a href="/practice/Hilo.html" class="sidebar-link">Hilo 使用记录</a></li><li><a href="/practice/canvas.html" class="sidebar-link">canvas 绘制</a></li><li><a href="/practice/react.html" class="sidebar-link">React + AntD 实战后台管理</a></li><li><a href="/practice/zrender.html" aria-current="page" class="active sidebar-link">ZRender 源码解析</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a href="/practice/zrender.html#渲染流程" class="sidebar-link">渲染流程</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a href="/practice/zrender.html#_1-调用animation-start-方法" class="sidebar-link">1.调用animation.start()方法</a></li><li class="sidebar-sub-header"><a href="/practice/zrender.html#_2-启动动画更新函数" class="sidebar-link">2.启动动画更新函数</a></li><li class="sidebar-sub-header"><a href="/practice/zrender.html#_3-每次动画更新函数" class="sidebar-link">3.每次动画更新函数</a></li></ul></li><li class="sidebar-sub-header"><a href="/practice/zrender.html#动画流程" class="sidebar-link">动画流程</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a href="/practice/zrender.html#zrender-实例的-animate方法" class="sidebar-link">zrender 实例的.animate方法</a></li><li class="sidebar-sub-header"><a href="/practice/zrender.html#使用when-方法提供关键帧" class="sidebar-link">使用when()方法提供关键帧</a></li><li class="sidebar-sub-header"><a href="/practice/zrender.html#start-方法运行关键帧" class="sidebar-link">start()方法运行关键帧</a></li></ul></li><li class="sidebar-sub-header"><a href="/practice/zrender.html#无人机项目设计" class="sidebar-link">无人机项目设计</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a href="/practice/zrender.html#项目结构设计" class="sidebar-link">项目结构设计</a></li></ul></li></ul></li><li><a href="/practice/search.html" class="sidebar-link">基于 node.js 的全文搜索</a></li><li><a href="/practice/phaser.html" class="sidebar-link">使用phaser开发战旗游戏</a></li><li><a href="/practice/medicine.html" class="sidebar-link">小儿用药</a></li></ul> </aside> <main class="page"> <div class="theme-default-content content__default"><h1 id="zrender-源码解析"><a href="#zrender-源码解析" class="header-anchor">#</a> ZRender 源码解析</h1> <blockquote><p>无人机搜索项目打算使用百度 ECharts 团队开源的渲染框架<a href="https://ecomfe.github.io/zrender-doc/public/" target="_blank" rel="noopener noreferrer">Zrender<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a>，本着先从简单开始，找到的是 2013 年 zrender 的源码进行解析，注释都是中文，相对来说代码量少一些，方便阅读。</p></blockquote> <h2 id="渲染流程"><a href="#渲染流程" class="header-anchor">#</a> 渲染流程</h2> <h3 id="_1-调用animation-start-方法"><a href="#_1-调用animation-start-方法" class="header-anchor">#</a> 1.调用<code>animation.start()</code>方法</h3> <p>因为只有动画才会导致图形更新，因此更新方法在初始化动画中进行定义，<code>Zrender</code>类在构造函数中会调用<code>initAnimate()</code>方法，在<code>initAnimate()</code>方法内部, 会创建有动画变动的形状集合, 然后实例化<code>Animation</code>类，并调用它的<code>start()</code>方法。</p> <div class="language-js extra-class"><pre class="language-js"><code>    <span class="token comment">// 动画控制</span>
    <span class="token function">initAnimate</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token comment">// 需要变动的形状集合, 不变动的形状则不进行更新</span>
        <span class="token keyword">this</span><span class="token punctuation">.</span>animatingShapes <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
        <span class="token comment">// stage是绘制类, 需要提供update接口</span>
        <span class="token keyword">this</span><span class="token punctuation">.</span>animation <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Animation</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
            stage<span class="token operator">:</span> <span class="token punctuation">{</span>
                <span class="token comment">// 必须使用箭头函数, 否则this绑定会出错</span>
                <span class="token function-variable function">update</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
                    <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">update</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>animatingShapes<span class="token punctuation">)</span><span class="token punctuation">;</span>
                <span class="token punctuation">}</span>
            <span class="token punctuation">}</span>
        <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">this</span><span class="token punctuation">.</span>animation<span class="token punctuation">.</span><span class="token function">start</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
</code></pre></div><h3 id="_2-启动动画更新函数"><a href="#_2-启动动画更新函数" class="header-anchor">#</a> 2.启动动画更新函数</h3> <p><code>Animation</code>类的<code>start()</code>函数定义如下. 调用<code>start()</code>函数之后, 会进入, 主要是运行内部的<code>step()</code>函数, 最后一行<code>requrestAnimationFrame(step)</code>主要是为了启动, 在<code>requrestAnimationFrame</code>方法内部, 会再次调用<code>step()</code>方法, 达到不断运行<code>step()</code>的目的. 而在<code>step</code>中, <strong>最重要的是调用了<code>update()</code>函数</strong>.</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token function-variable function">start</span><span class="token operator">:</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">const</span> self <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">;</span>
        <span class="token comment">// 开始运行设置为true, 修改animation的_running可以停止</span>
        <span class="token keyword">this</span><span class="token punctuation">.</span>_running <span class="token operator">=</span> <span class="token boolean">true</span><span class="token punctuation">;</span>
        <span class="token comment">// 下述方法只能保证每一帧运行完之后,间隔1000/60毫秒执行下一次,</span>
        <span class="token comment">// 但是由于运行update()也需要时间, 所以并不能保证最后结果是60帧</span>
        <span class="token keyword">function</span> <span class="token function">step</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token keyword">if</span> <span class="token punctuation">(</span>self<span class="token punctuation">.</span>_running<span class="token punctuation">)</span> <span class="token punctuation">{</span>
                self<span class="token punctuation">.</span><span class="token function">update</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
                <span class="token function">requrestAnimationFrame</span><span class="token punctuation">(</span>step<span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token punctuation">}</span>
        <span class="token punctuation">}</span>

        <span class="token function">requrestAnimationFrame</span><span class="token punctuation">(</span>step<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
</code></pre></div><p><code>requrestAnimationFrame</code>函数定义</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token comment">// 需传入回调函数,保证每隔1000/60ms执行一次回调函数</span>
<span class="token keyword">const</span> requrestAnimationFrame <span class="token operator">=</span>
  window<span class="token punctuation">.</span>requrestAnimationFrame <span class="token operator">||</span>
  window<span class="token punctuation">.</span>mozRequestAnimationFrame <span class="token operator">||</span>
  window<span class="token punctuation">.</span>webkitRequestAnimationFrame <span class="token operator">||</span>
  <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">callback</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    window<span class="token punctuation">.</span><span class="token function">setTimeout</span><span class="token punctuation">(</span>callback<span class="token punctuation">,</span> <span class="token number">1000</span> <span class="token operator">/</span> <span class="token number">60</span><span class="token punctuation">)</span>
  <span class="token punctuation">}</span>
</code></pre></div><h3 id="_3-每次动画更新函数"><a href="#_3-每次动画更新函数" class="header-anchor">#</a> 3.每次动画更新函数</h3> <p>每次调用动画更新函数<code>Animation.update()</code>, 代码如下.由于有些动画是靠插值进行, 所以用了很多控制器进行控制, 但是最重要的更新代码还是中间的<code>stage.update()</code>方法, 该方法才是进行视图的更新, <code>stage.update()</code>方法是初始化<code>Animation</code>时传入的.</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token comment">// 动画更新函数, 每隔16ms运行一次</span>
    <span class="token function-variable function">update</span><span class="token operator">:</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token comment">// 记录开始时间</span>
        <span class="token keyword">const</span> time <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Date</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getTime</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token comment">// 控制池</span>
        <span class="token keyword">const</span> cp <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>_controllerPool<span class="token punctuation">;</span>
        <span class="token comment">// 控制池大小</span>
        <span class="token keyword">let</span> len <span class="token operator">=</span> cp<span class="token punctuation">.</span>length<span class="token punctuation">;</span>
        <span class="token comment">// 设置插值的位置的大小</span>
        <span class="token keyword">const</span> deferredEvents <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
        <span class="token keyword">const</span> deferredCtls <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
        <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">let</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator">&lt;</span> len<span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token keyword">const</span> controller <span class="token operator">=</span> cp<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">;</span>
            <span class="token comment">// 根据当前时间获得控制器</span>
            <span class="token keyword">const</span> e <span class="token operator">=</span> controller<span class="token punctuation">.</span><span class="token function">step</span><span class="token punctuation">(</span>time<span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token comment">// 需要在stage.update之后调用的事件，例如destroy</span>
            <span class="token keyword">if</span> <span class="token punctuation">(</span>e<span class="token punctuation">)</span> <span class="token punctuation">{</span>
                deferredEvents<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span>e<span class="token punctuation">)</span><span class="token punctuation">;</span>
                deferredCtls<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span>controller<span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token punctuation">}</span>
        <span class="token punctuation">}</span>
        <span class="token comment">// 调用stage的update方法</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>stage <span class="token operator">&amp;&amp;</span> <span class="token keyword">this</span><span class="token punctuation">.</span>stage<span class="token punctuation">.</span>update <span class="token operator">&amp;&amp;</span> <span class="token keyword">this</span><span class="token punctuation">.</span>_controllerPool<span class="token punctuation">.</span>length
        <span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token keyword">this</span><span class="token punctuation">.</span>stage<span class="token punctuation">.</span><span class="token function">update</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
        <span class="token comment">// 删除动画完成的控制器</span>
        <span class="token keyword">const</span> newArray <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
        <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">let</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator">&lt;</span> len<span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>cp<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">.</span>_needsRemove<span class="token punctuation">)</span> <span class="token punctuation">{</span>
                newArray<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span>cp<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
                cp<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">.</span>_needsRemove <span class="token operator">=</span> <span class="token boolean">false</span><span class="token punctuation">;</span>
            <span class="token punctuation">}</span>
        <span class="token punctuation">}</span>
        <span class="token keyword">this</span><span class="token punctuation">.</span>_controllerPool <span class="token operator">=</span> newArray<span class="token punctuation">;</span>

        len <span class="token operator">=</span> deferredEvents<span class="token punctuation">.</span>length<span class="token punctuation">;</span>
        <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">var</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator">&lt;</span> len<span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
            deferredCtls<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token function">fire</span><span class="token punctuation">(</span>deferredEvents<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
        <span class="token comment">// 每次动画运行完的回调</span>
        <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">onframe</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
</code></pre></div><p>调用<code>stage.update()</code>方法时, 各个形状的属性已经被改变了, 像位置/方向/大小/颜色等, 然后调用的是<code>painter</code>实例中的<code>update()</code>方法进行绘图, 这个方法很简短:</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token comment">/**
 * 视图更新
 * @param {Array} shapeList 需要更新的图形元素列表,
 *                          不进入这个list的元素则不进行更新
 * @param {Function} callback  视图更新后回调函数
 */</span>
<span class="token keyword">function</span> <span class="token function">update</span><span class="token punctuation">(</span><span class="token parameter">shapeList<span class="token punctuation">,</span> callback</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">var</span> shape
  <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">var</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">,</span> l <span class="token operator">=</span> shapeList<span class="token punctuation">.</span>length<span class="token punctuation">;</span> i <span class="token operator">&lt;</span> l<span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    shape <span class="token operator">=</span> shapeList<span class="token punctuation">[</span>i<span class="token punctuation">]</span>
    storage<span class="token punctuation">.</span><span class="token function">mod</span><span class="token punctuation">(</span>shape<span class="token punctuation">.</span>id<span class="token punctuation">,</span> shape<span class="token punctuation">)</span>
  <span class="token punctuation">}</span>
  <span class="token function">refresh</span><span class="token punctuation">(</span>callback<span class="token punctuation">)</span>
  <span class="token keyword">return</span> self
<span class="token punctuation">}</span>
</code></pre></div><h2 id="动画流程"><a href="#动画流程" class="header-anchor">#</a> 动画流程</h2> <p>在<code>src/index.js</code>文件中, <code>zrender</code>实例链式调用了<code>when</code>,<code>done</code>和<code>start</code>等一系列方法, 代码如下:</p> <div class="language-js extra-class"><pre class="language-js"><code>zr<span class="token punctuation">.</span><span class="token function">animate</span><span class="token punctuation">(</span>uav<span class="token punctuation">.</span>id<span class="token punctuation">,</span> <span class="token string">''</span><span class="token punctuation">)</span>
  <span class="token punctuation">.</span><span class="token function">when</span><span class="token punctuation">(</span><span class="token number">1000</span><span class="token punctuation">,</span> <span class="token punctuation">{</span>
    position<span class="token operator">:</span> <span class="token punctuation">[</span><span class="token number">200</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span>
  <span class="token punctuation">.</span><span class="token function">when</span><span class="token punctuation">(</span>
    <span class="token number">2000</span><span class="token punctuation">,</span>
    <span class="token punctuation">{</span>
      position<span class="token operator">:</span> <span class="token punctuation">[</span><span class="token number">200</span><span class="token punctuation">,</span> <span class="token number">200</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
    <span class="token punctuation">}</span><span class="token punctuation">,</span>
    <span class="token string">'BounceIn'</span>
  <span class="token punctuation">)</span>
  <span class="token punctuation">.</span><span class="token function">when</span><span class="token punctuation">(</span><span class="token number">3000</span><span class="token punctuation">,</span> <span class="token punctuation">{</span>
    position<span class="token operator">:</span> <span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">200</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span>
  <span class="token punctuation">.</span><span class="token function">when</span><span class="token punctuation">(</span><span class="token number">4000</span><span class="token punctuation">,</span> <span class="token punctuation">{</span>
    position<span class="token operator">:</span> <span class="token punctuation">[</span><span class="token number">100</span><span class="token punctuation">,</span> <span class="token number">100</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span>
  <span class="token punctuation">.</span><span class="token function">done</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    zr<span class="token punctuation">.</span><span class="token function">animate</span><span class="token punctuation">(</span>uav<span class="token punctuation">.</span>id<span class="token punctuation">)</span>
      <span class="token punctuation">.</span><span class="token function">when</span><span class="token punctuation">(</span><span class="token number">2000</span><span class="token punctuation">,</span> <span class="token punctuation">{</span>
        rotation<span class="token operator">:</span> <span class="token punctuation">[</span>Math<span class="token punctuation">.</span><span class="token constant">PI</span> <span class="token operator">*</span> <span class="token number">2</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
      <span class="token punctuation">}</span><span class="token punctuation">)</span>
      <span class="token punctuation">.</span><span class="token function">start</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span>
  <span class="token punctuation">.</span><span class="token function">start</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
</code></pre></div><p>下面详细分析一下这段代码内部的工作原理.</p> <h3 id="zrender-实例的-animate方法"><a href="#zrender-实例的-animate方法" class="header-anchor">#</a> zrender 实例的<code>.animate</code>方法</h3> <p>zrender 实例的.animate()方法实现很长, 源码如下所示, 输入参数有 3 个, <code>shapeId</code>必填, 代表形状的 id 值, 用于获取形状, 后面的<code>path</code>参数主要表示动画改变的属性地址, 例如该变动的是 <code>shape.style</code>(例如让他变大), 那么这里就填写<code>'style'</code>. 源码首先获取改变属性的对象<code>target</code>, 涉及到判断<code>path</code>是否存在来获取<code>shape</code>对应的属性. 找到<code>target</code>对象之后, 初始化对应形状的动画数量属性<code>__aniCount</code>, 将<code>shape.__aniCount</code>置为 0, 并将其加入到<code>zr.animatingShapes</code>中, 表明这个形状需要动画(不需要动画后期不更新, 以提升效率).</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token function">animate</span><span class="token punctuation">(</span><span class="token parameter">shapeId<span class="token punctuation">,</span> path<span class="token punctuation">,</span> loop</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token comment">// 获取形状</span>
        <span class="token keyword">const</span> shape <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>storage<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span>shapeId<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span>shape<span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token comment">// 要变更的目标,默认是shape, path存在则给其他属性</span>
            <span class="token keyword">let</span> target <span class="token operator">=</span> <span class="token keyword">undefined</span><span class="token punctuation">;</span>
            <span class="token keyword">if</span> <span class="token punctuation">(</span>path<span class="token punctuation">)</span> <span class="token punctuation">{</span>
                <span class="token keyword">const</span> pathSplitted <span class="token operator">=</span> path<span class="token punctuation">.</span><span class="token function">split</span><span class="token punctuation">(</span><span class="token string">'.'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
                <span class="token keyword">let</span> prop <span class="token operator">=</span> shape<span class="token punctuation">;</span>
                <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">let</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">,</span> l <span class="token operator">=</span> pathSplitted<span class="token punctuation">.</span>length<span class="token punctuation">;</span> i <span class="token operator">&lt;</span> l<span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
                    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>prop<span class="token punctuation">)</span> <span class="token punctuation">{</span>
                        <span class="token keyword">continue</span><span class="token punctuation">;</span>
                    <span class="token punctuation">}</span>
                    prop <span class="token operator">=</span> prop<span class="token punctuation">[</span>pathSplitted<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
                <span class="token punctuation">}</span>
                <span class="token keyword">if</span> <span class="token punctuation">(</span>prop<span class="token punctuation">)</span> <span class="token punctuation">{</span>
                    target <span class="token operator">=</span> prop<span class="token punctuation">;</span>
                <span class="token punctuation">}</span>
            <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
                target <span class="token operator">=</span> shape<span class="token punctuation">;</span>
            <span class="token punctuation">}</span>
            <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>target<span class="token punctuation">)</span> <span class="token punctuation">{</span>
                logger<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">Property </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>path<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string"> is not existed in shape </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>shapeId<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">//</span>
                <span class="token keyword">return</span><span class="token punctuation">;</span>
            <span class="token punctuation">}</span>
            <span class="token comment">// 动态给图形增加__aniCount的属性, 表示动画数</span>
            <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">typeof</span> <span class="token punctuation">(</span>shape<span class="token punctuation">.</span>__aniCount<span class="token punctuation">)</span> <span class="token operator">===</span> <span class="token string">'undefined'</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
                <span class="token comment">// 正在进行的动画记数</span>
                shape<span class="token punctuation">.</span>__aniCount <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span>
            <span class="token punctuation">}</span>
            <span class="token comment">// 动画数为0则证明是刚开始, 则可以加入到需要变动的形状集合</span>
            <span class="token keyword">if</span> <span class="token punctuation">(</span>shape<span class="token punctuation">.</span>__aniCount <span class="token operator">===</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
                <span class="token keyword">this</span><span class="token punctuation">.</span>animatingShapes<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span>shape<span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token punctuation">}</span>
            <span class="token comment">// 动画数加1</span>
            shape<span class="token punctuation">.</span>__aniCount<span class="token operator">++</span><span class="token punctuation">;</span>
            <span class="token comment">// target是图形, loop是是否循环</span>
            <span class="token keyword">return</span> <span class="token keyword">this</span><span class="token punctuation">.</span>animation<span class="token punctuation">.</span><span class="token function">animate</span><span class="token punctuation">(</span>target<span class="token punctuation">,</span> loop<span class="token punctuation">)</span>
                <span class="token punctuation">.</span><span class="token function">done</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
                    shape<span class="token punctuation">.</span>__aniCount<span class="token operator">--</span><span class="token punctuation">;</span>
                    <span class="token keyword">if</span> <span class="token punctuation">(</span>shape<span class="token punctuation">.</span>__aniCount <span class="token operator">===</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
                        <span class="token comment">// 从animatingShapes里移除</span>
                        <span class="token keyword">const</span> idx <span class="token operator">=</span> util<span class="token punctuation">.</span><span class="token function">indexOf</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>animatingShapes<span class="token punctuation">,</span> shape<span class="token punctuation">)</span><span class="token punctuation">;</span>
                        <span class="token keyword">this</span><span class="token punctuation">.</span>animatingShapes<span class="token punctuation">.</span><span class="token function">splice</span><span class="token punctuation">(</span>idx<span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
                    <span class="token punctuation">}</span>
                <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
            logger<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">Shape </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>shapeId<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string"> not existed</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
    <span class="token punctuation">}</span><span class="token punctuation">;</span>
</code></pre></div><p>重点在于这个函数的返回值, 返回的是一个动画类<code>Animate</code>的<code>.animate()</code>返回值,生成的是一个<code>Deferred</code>对象, <code>Deferred</code>拥有整个 animate 实例, 因此可以利用<code>animate</code>控制<code>controller</code>, 具体函数实现如下:</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token function-variable function">animate</span><span class="token operator">:</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">target<span class="token punctuation">,</span> loop<span class="token punctuation">,</span> getter<span class="token punctuation">,</span> setter</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token comment">// 新生成一个deferred类, 只需要传变动的目标即可.</span>
        <span class="token keyword">var</span> deferred <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Deferred</span><span class="token punctuation">(</span>target<span class="token punctuation">,</span> loop<span class="token punctuation">,</span> getter<span class="token punctuation">,</span> setter<span class="token punctuation">)</span><span class="token punctuation">;</span>
        deferred<span class="token punctuation">.</span>animation <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">;</span>
        <span class="token keyword">return</span> deferred<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
</code></pre></div><p>在返回的<code>deferred</code>实例的基础上, 又增加了<code>done()</code>方法, 传入了回调函数(这个回调函数是在动画销毁的时候进行调用的, 目的是把形状从<code>animatingShapes</code>中去掉, 不再进行更新渲染), 返回值依然是<code>defered</code>对象.</p> <h3 id="使用when-方法提供关键帧"><a href="#使用when-方法提供关键帧" class="header-anchor">#</a> 使用<code>when()</code>方法提供关键帧</h3> <p>下面用户就可以对<code>deferred</code>增加动画了, 这里<code>zrender</code>主要是采用关键帧绘制动画, 也就是用户只需要告诉元素在关键的那一秒需要出现在哪个位置, 那么<code>zrender</code>会自动将中间的部分进行补帧, 把中间需要的帧数给补齐, 这个提供关键帧的方法就是<code>when()</code>, 源码如下</p> <div class="language-js extra-class"><pre class="language-js"><code>    <span class="token comment">// 使用when方法设置帧</span>
    <span class="token function-variable function">when</span><span class="token operator">:</span> <span class="token keyword">function</span> <span class="token punctuation">(</span>time <span class="token comment">/* ms */</span><span class="token punctuation">,</span> props<span class="token punctuation">,</span> easing<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">var</span> propName <span class="token keyword">in</span> props<span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token comment">// 判断在_tracks中是否存在, 如果不存在, 先记录初始状态</span>
            <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span><span class="token keyword">this</span><span class="token punctuation">.</span>_tracks<span class="token punctuation">[</span>propName<span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
                <span class="token keyword">this</span><span class="token punctuation">.</span>_tracks<span class="token punctuation">[</span>propName<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
                <span class="token comment">// 初始状态, 并没有easing(缓动)属性</span>
                <span class="token keyword">this</span><span class="token punctuation">.</span>_tracks<span class="token punctuation">[</span>propName<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
                    time<span class="token operator">:</span> <span class="token number">0</span><span class="token punctuation">,</span>
                    value<span class="token operator">:</span> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">_getter</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>_target<span class="token punctuation">,</span> propName<span class="token punctuation">)</span>
                <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token punctuation">}</span>
            <span class="token comment">// 增加关键帧情况</span>
            <span class="token keyword">this</span><span class="token punctuation">.</span>_tracks<span class="token punctuation">[</span>propName<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
                time<span class="token operator">:</span> time<span class="token punctuation">,</span>
                value<span class="token operator">:</span> props<span class="token punctuation">[</span>propName<span class="token punctuation">]</span><span class="token punctuation">,</span>
                easing<span class="token operator">:</span> easing
            <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
        <span class="token keyword">return</span> <span class="token keyword">this</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
</code></pre></div><p>提供关键帧后, zrender 会在对应<code>Deferred</code>(Deferred 跟目标对象相对应, 一个目标对应一个<code>Deferred</code>)的<code>_tracks</code>属性中增加关键帧的时间, 需要达到的值和缓动函数, 从源码来看, <code>this._tracks[0]</code>表示的是 0 时刻的初始状态.</p> <h3 id="start-方法运行关键帧"><a href="#start-方法运行关键帧" class="header-anchor">#</a> <code>start()</code>方法运行关键帧</h3> <p>通过调用<code>deferred.start()</code>方法运行关键帧, 实现很简单, 就是根据<code>_track</code>中的信息, 在<code>deferred._controllerList</code>新增每个控制器, 每个控制器用来控制<code>_track</code>相邻两部分转换的状态, 所有的控制器都加到<code>animation</code>实例中, 由 <code>animation</code> 统一管理.</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token function-variable function">start</span><span class="token operator">:</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">var</span> self <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">;</span>
        <span class="token keyword">var</span> delay<span class="token punctuation">;</span>
        <span class="token keyword">var</span> track<span class="token punctuation">;</span>
        <span class="token keyword">var</span> trackMaxTime<span class="token punctuation">;</span>

        <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">var</span> propName <span class="token keyword">in</span> <span class="token keyword">this</span><span class="token punctuation">.</span>_tracks<span class="token punctuation">)</span> <span class="token punctuation">{</span>
            delay <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span>
            track <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>_tracks<span class="token punctuation">[</span>propName<span class="token punctuation">]</span><span class="token punctuation">;</span>
            <span class="token comment">// 获得的track是数组, 所以需要判断数组情况</span>
            <span class="token keyword">if</span> <span class="token punctuation">(</span>track<span class="token punctuation">.</span>length<span class="token punctuation">)</span> <span class="token punctuation">{</span>
                <span class="token comment">// 获得最大时间, 不过这里只考虑用户按时间大小插入, 不考虑非线性情况</span>
                trackMaxTime <span class="token operator">=</span> track<span class="token punctuation">[</span>track<span class="token punctuation">.</span>length <span class="token operator">-</span> <span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">.</span>time<span class="token punctuation">;</span>
            <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
                <span class="token keyword">continue</span><span class="token punctuation">;</span>
            <span class="token punctuation">}</span>
            <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">var</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator">&lt;</span> track<span class="token punctuation">.</span>length <span class="token operator">-</span> <span class="token number">1</span><span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
                <span class="token keyword">var</span> now <span class="token operator">=</span> track<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">,</span>
                    next <span class="token operator">=</span> track<span class="token punctuation">[</span>i <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">;</span>

                <span class="token keyword">var</span> controller <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Controller</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
                    target<span class="token operator">:</span> self<span class="token punctuation">.</span>_target<span class="token punctuation">,</span>
                    life<span class="token operator">:</span> next<span class="token punctuation">.</span>time <span class="token operator">-</span> now<span class="token punctuation">.</span>time<span class="token punctuation">,</span>
                    delay<span class="token operator">:</span> delay<span class="token punctuation">,</span>
                    loop<span class="token operator">:</span> self<span class="token punctuation">.</span>_loop<span class="token punctuation">,</span>
                    <span class="token comment">// gap是循环的间隔时间,也就是loop如果为真的话,这一段下次多久开始</span>
                    <span class="token comment">// 用整个这段时间 - 这个控制器用时, 就是等待时间</span>
                    gap<span class="token operator">:</span> trackMaxTime <span class="token operator">-</span> <span class="token punctuation">(</span>next<span class="token punctuation">.</span>time <span class="token operator">-</span> now<span class="token punctuation">.</span>time<span class="token punctuation">)</span><span class="token punctuation">,</span>
                    easing<span class="token operator">:</span> next<span class="token punctuation">.</span>easing<span class="token punctuation">,</span>
                    onframe<span class="token operator">:</span> <span class="token function">createOnframe</span><span class="token punctuation">(</span>now<span class="token punctuation">,</span> next<span class="token punctuation">,</span> propName<span class="token punctuation">)</span><span class="token punctuation">,</span>
                    ondestroy<span class="token operator">:</span> ondestroy
                <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
                <span class="token keyword">this</span><span class="token punctuation">.</span>_controllerList<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span>controller<span class="token punctuation">)</span><span class="token punctuation">;</span>

                <span class="token keyword">this</span><span class="token punctuation">.</span>_controllerCount<span class="token operator">++</span><span class="token punctuation">;</span>
                delay <span class="token operator">=</span> next<span class="token punctuation">.</span>time<span class="token punctuation">;</span>

                self<span class="token punctuation">.</span>animation<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span>controller<span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token punctuation">}</span>
        <span class="token punctuation">}</span>
        <span class="token keyword">return</span> <span class="token keyword">this</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
</code></pre></div><p>加入 <code>animation</code> 实例的<code>controller</code>会被加入到控制器池<code>animation._controllerPool</code>中, 然后在 <code>animation</code> 的每次更新中, 根据用时来判断当前到那个程度 schedule, 然后发送<code>onframe</code>事件, 调用<code>controller</code>构造函数时传入的<code>onframe</code>函数进行处理.</p> <h2 id="无人机项目设计"><a href="#无人机项目设计" class="header-anchor">#</a> 无人机项目设计</h2> <p>参考 zrender 项目, 按照 mvc 框架分为 3 块, 分别是:</p> <ol><li>model 模型类,用来存储无人机和其他对象的相关数据,</li> <li>view 视图类, 用来绘制无人机和其他环境对象</li> <li>controller 控制类, 用来控制无人机策略</li></ol> <p>数据流向是 controller → model → view</p> <h3 id="项目结构设计"><a href="#项目结构设计" class="header-anchor">#</a> 项目结构设计</h3> <ol><li><strong>/model</strong>: 模型(model)类, 存放实体类文件
<ol><li><code>UAV.js</code>: 定义无人机的类, 包括旋翼无人机和固定翼无人机两类, 主要属性有 position(当前位置), angel(当前朝向), v(速度大小), 通过当前朝向和速度大小可以将速度分解为水平和垂直 2 个方向, 得到速度向量, 绘制时调用常用形状中的某个.内部有个<code>_forceList</code>, 每次更新时会把所有力加起来一并进行计算</li> <li><code>Cell.js</code>: 单元格类, 每个搜索格的属性</li> <li><code>Target.js</code>: 目标类, 搜索/打击的目标, 主要属性有: 目标价值, 移动速度, 初始移动方向, 是否随机改变速度,</li> <li><code>Threat.js</code>: 威胁类, 包括障碍物, 反无人机雷达等, 主要属性有: 位置, 威胁范围</li></ol></li> <li><strong>/storage</strong>: 存储类
<ol><li><code>flock.js</code>: 无人机群, 存储所有的无人机实例</li> <li><code>Grid.js</code>: 区域类, 内部有个<code>_cellPool</code>属性, 用来放置每个网格, 每个网格有自己的默认属性(网格的颜色, 对角坐标, 网格初始参数), 也可以传入用户参数, 绘制时使用</li> <li><code>influence.js</code>: 影响因素类, 存放所有实例化后的目标/威胁.</li></ol></li> <li><strong>/shape</strong>: 存放常用形状
<ol><li><code>base.js</code>: 形状基类</li> <li>其他形状类, 主要有矩形\三角形\多边形\五角星\椭圆形</li></ol></li> <li><strong>/handler</strong>: 控制器(controller)类, 设置各种无人机相互之间, 和环境之间交互方法,
<ol><li><code>strategy.js</code>: 设置无人机的各类策略, 传入环境参数, 判断无人机下一个要去的地点</li> <li><code>interaction.js</code>: 交互类, 威胁和其他无人机对当前无人机的影响, 增加力到<code>_forceList</code>中去, 更新网格/威胁/目标的各类情况, 这里主要修改<code>flock</code>和<code>environment</code>实例, 修改后的对象 id 都放到一个 list 中, <code>Painter.js</code>根据这修改后的对象内容再进行渲染</li></ol></li> <li><strong>/view</strong>: 视图(view)类, 主要放初始化和绘制 canvas 类
<ol><li><code>Painter.js</code>: 绘制类</li> <li><code>canvasManager</code>: canvas 管理类</li></ol></li> <li><strong>/calc</strong>: 算法类
<ol><li><code>PSO.js</code>: 标准粒子群算法</li> <li><code>CLPSO.js</code>: CLPSO 算法</li> <li><code>BBO.js</code>: 生物地理学算法</li></ol></li> <li><strong>/util</strong>: 工具类目录
<ol><li><code>logger.js</code>: 日志文件</li> <li><code>vector.js</code>: 二维向量工具类</li> <li><code>color.js</code>: 颜色辅助类</li> <li><code>area.js</code>: 图形空间辅助类</li> <li><code>math.js</code>: 数学计算辅助类</li> <li><code>matrix.js</code>: 矩阵计算类</li> <li><code>tool.js</code>: js 常用工具</li></ol></li> <li><strong>/event</strong>: 事件相关目录
<ol><li><code>help.js</code>: 事件工具类</li> <li><code>Dispatcher</code>: 事件分发类</li></ol></li> <li>/: 根目录</li> <li><code>simulation.js</code>: 仿真类, 主类, 提供 Simulation 类, 并实例化 simulation. 包含<code>emulate()</code>函数, 表示开始仿真, 内部有<code>flock</code>实例, <code>environment</code>实例, <code>Painter</code>实例和<code>interaction</code>实例</li> <li><code>config.js</code>: 配置项, (1) 无人机的各类配置, 有最大速度, 最小速度, 最大加速度, 最大转弯速度等.(2)网格配置: 网格尺寸.(3)支持的事件类型</li></ol></div> <footer class="page-edit"><!----> <div class="last-updated"><span class="prefix">更新于:</span> <span class="time">6/29/2020, 11:15:28 PM</span></div></footer> <div class="page-nav"><p class="inner"><span class="prev">
      ←
      <a href="/practice/react.html" class="prev">
        React + AntD 实战后台管理
      </a></span> <span class="next"><a href="/practice/search.html">
        基于 node.js 的全文搜索
      </a>
      →
    </span></p></div> </main></div><div class="global-ui"><!----></div></div>
    <script src="/assets/js/app.9e067b79.js" defer></script><script src="/assets/js/2.983eb755.js" defer></script><script src="/assets/js/70.11ee4e27.js" defer></script>
  </body>
</html>
